﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using CustomORM.DataAccess;
using CustomORM.Mapping;
using CustomORM.Repository;
using CustomORM.Tests.CustomORM.Mapping;
using Rhino.Mocks;
using NUnit.Framework;

namespace CustomORM.Tests.CustomORM
{
    [TestFixture]
    public class RepositoryBaseTests
    {
        [Test]
        public void HandleChildren_ParentHasTwoNewChildren_ChildrenIdsUpdatedInsertForChildrenCalledNoDelete()
        {
            //setup mapping
            MagicMapper.RemoveMapper(typeof(ParentEntity));
            var parentMapper = new EntityToTableMapperExposed<ParentEntity>();
            parentMapper.Table("PARENT");
            parentMapper.Id(x => x.Id, "PARENT_ID");
            parentMapper.Map(x => x.Name, "NAME");
            parentMapper.HasMany(x => x.Kids, "PARENT_ID");
            MagicMapper.RemoveMapper(typeof(KidEntity));
            var kidMapper = new EntityToTableMapperExposed<KidEntity>();
            kidMapper.Table("KID");
            kidMapper.Id(x => x.Id, "KID_ID");
            kidMapper.Map(x => x.ParentId, "PARENT_ID");
            kidMapper.Map(x => x.Name, "NAME");
            // new entity
            var parent = new ParentEntity() { Kids = new List<KidEntity>() { new KidEntity() { Name = "A" }, new KidEntity() { Name = "B" } } };

            var dataAccess = MockRepository.GenerateStub<IDataAccess>();
            dataAccess.Stub(x => x.InsertUpdateRow(Arg<RecordData>.Is.Anything)).Return(5);
            dataAccess.Stub(x => x.DeleteRow(Arg<RecordData>.Is.Anything));
            var providerBase = new RepositoryBaseExposed(dataAccess);

            RecordData record = parentMapper.GetRecordData(parent);
            providerBase.HandleSaveChildren(parent, record, parentMapper, 99);

            Assert.That(parent.Kids[0].ParentId, Is.EqualTo(99));
            Assert.That(parent.Kids[1].ParentId, Is.EqualTo(99));

            dataAccess.AssertWasCalled(x => x.InsertUpdateRow(Arg<RecordData>.Is.Anything), options => options.Repeat.Twice());
            dataAccess.AssertWasNotCalled(x => x.DeleteRow(Arg<RecordData>.Is.Anything));
        }

        [Test]
        public void HandleChildren_ParentHasTwoChildrenAndThreeOldAndOneMatching_TwoSavesTwoDeletesAreGoingToHappen()
        {
            //setup mapping
            MagicMapper.RemoveMapper(typeof(ParentEntity));
            var parentMapper = new EntityToTableMapperExposed<ParentEntity>();
            parentMapper.Table("PARENT");
            parentMapper.Id(x => x.Id, "PARENT_ID");
            parentMapper.Map(x => x.Name, "NAME");
            parentMapper.HasMany(x => x.Kids, "PARENT_ID")
                // THID IS DIFFERENCE TO THE PREVIOUS METHOD
                .DeleteMissedItems();
            MagicMapper.RemoveMapper(typeof(KidEntity));
            var kidMapper = new EntityToTableMapperExposed<KidEntity>();
            kidMapper.Table("KID");
            kidMapper.Id(x => x.Id, "KID_ID");
            kidMapper.Map(x => x.ParentId, "PARENT_ID");
            kidMapper.Map(x => x.Name, "NAME");
            // new parent that has two kids, one new and one existing
            var parent = new ParentEntity() { Kids = new List<KidEntity>() { new KidEntity() { Name = "NewKid" }, new KidEntity() { Id = 7, Name = "John" } } };

            // Database thinks that parent has 3 kids, including John
            var table = new DataTable();
            table.Columns.Add("KID_ID", typeof(int));
            table.Columns.Add("PARENT_ID", typeof(int));
            table.Columns.Add("NAME", typeof(string));
            table.Columns.Add("BIRTH_DATE", typeof(DateTime));
            table.LoadDataRow(new object[] { 7, 1, "John" }, true);
            table.LoadDataRow(new object[] { 8, 1, "Alien1" }, true);
            table.LoadDataRow(new object[] { 9, 1, "Alien2" }, true);


            var dataAccess = MockRepository.GenerateStub<IDataAccess>();
            dataAccess.Stub(x => x.SelectSQL(Arg<string>.Is.Anything, Arg<Dictionary<string,object>>.Is.Anything)).Return(table);
            dataAccess.Stub(x => x.InsertUpdateRow(Arg<RecordData>.Is.Anything)).Return(5);
            dataAccess.Stub(x => x.DeleteRow(Arg<RecordData>.Is.Anything));

            var providerBase = new RepositoryBaseExposed(dataAccess);

            RecordData record = parentMapper.GetRecordData(parent);
            providerBase.HandleSaveChildren(parent, record, parentMapper, 99);

            Assert.That(parent.Kids.Count, Is.EqualTo(2));
            Assert.That(parent.Kids.Where(x => x.ParentId == 99).Count(), Is.EqualTo(2));
            //Assert.That(parent.Kids.Where(x => x.Id == 5).Count(), Is.EqualTo(1));
            //Assert.That(parent.Kids.Where(x => x.Id == 7).Count(), Is.EqualTo(1));

            // we updated John and inserted NewKid
            dataAccess.AssertWasCalled(x => x.InsertUpdateRow(Arg<RecordData>.Is.Anything), options => options.Repeat.Twice());
            // now we deleted two Aliens
            dataAccess.AssertWasCalled(x => x.DeleteRow(Arg<RecordData>.Is.Anything), options => options.Repeat.Twice());
        }

        [Test]
        public void Fetch_FetchesEntity_GeneratedCorrectSql()
        {
            MagicMapper.RemoveMapper(typeof(KidEntity));
            var kidMapper = new EntityToTableMapperExposed<KidEntity>();
            kidMapper.Table("KID");
            kidMapper.Id(x => x.Id, "KID_ID");
            kidMapper.Map(x => x.ParentId, "PARENT_ID");
            kidMapper.Map(x => x.Name, "NAME");

            var dataAccess = MockRepository.GenerateStub<IDataAccess>();
            dataAccess.Stub(x => x.InsertUpdateRow(Arg<RecordData>.Is.Anything)).Return(5);
            dataAccess.Stub(x => x.DeleteRow(Arg<RecordData>.Is.Anything));
            var providerBase = new RepositoryBaseExposed(dataAccess);

            //var keyValue = 5;
            //providerBase.Fetch(object keyValue);
        }

        [Test]
        public void CreateCriteria_ExpectedNewCriteriaCreatedWithAllFieldPopulated()
        {
            MagicMapper.RemoveMapper(typeof(ParentEntity));
            var parentEntityMap = new ParentEntityMap();
            var dataAccess = MockRepository.GenerateStub<IDataAccess>();
            var repository = new RepositoryBaseExposed(dataAccess);

            Criteria criteria = repository.CreateCriteria(typeof(ParentEntity), "parent");

            Assert.That(criteria, Is.Not.Null);
            Assert.That(criteria.Participants, Is.Not.Null);
            Assert.That(criteria.Participants[0].Mapper, Is.TypeOf(typeof(ParentEntityMap)));
            Assert.That(criteria.Participants[0].Alias, Is.Not.Null);
            Assert.That(criteria.Participants[0].Alias, Is.EqualTo("parent"));
        }

        [Test]
        public void CreateCriteriaForParentAndjoinChildren_CriteriaAllowsFluentSysntaxes()
        {
            MagicMapper.RemoveMapper(typeof(ParentEntity));
            MagicMapper.RemoveMapper(typeof(KidEntity));
            new ParentEntityMap();
            new KidEntityMap();
            
            var dataAccess = MockRepository.GenerateStub<IDataAccess>();
            var repository = new RepositoryBaseExposed(dataAccess);

            Criteria criteria = repository.CreateCriteria(typeof(ParentEntity), "parent")
                .AddEntity(typeof(KidEntity), "kid");

            Assert.That(criteria, Is.Not.Null);
            Assert.That(criteria.Participants, Is.Not.Null);
            Assert.That(criteria.Participants.Count, Is.GreaterThan(0));
            Assert.That(criteria.Participants[1].Mapper, Is.TypeOf(typeof(KidEntityMap)));
            Assert.That(criteria.Participants[1].Alias, Is.EqualTo("kid"));
        }

        //[Test]
        //public void CreateCriteriaForParentAndjoinChildren_ExecuteIt()
        //{
        //    MagicMapper.RemoveMapper(typeof(ParentEntity));
        //    MagicMapper.RemoveMapper(typeof(KidEntity));
        //    new ParentEntityMap();
        //    new KidEntityMap();

        //    var dataTable = new DataTable();
        //    var dataAccess = MockRepository.GenerateStub<IDataAccess>();
        //    dataAccess.Stub(x => x.SelectSQL(Arg<string>.Is.Anything)).Return(dataTable);
        //    var repository = new RepositoryBaseExposed(dataAccess);

        //    var parentId = 7;
        //    Criteria criteria = new Criteria(typeof(ParentEntity), "p")
        //        .AddEntity(typeof(KidEntity), "k")
        //        .AddSQL("join KID k on k.PARENT_ID = p.PARENT_ID where PARENT_ID = {0}", parentId);

        //    var returnedDataTable = repository.ExecuteCriteria(criteria);

        //    Assert.That(dataTable, Is.EqualTo(returnedDataTable));
        //}
    }
}